﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CodeC.Infrastructure.ViewModelBase;
using CodeC.Interfaces.UI;
using System.ComponentModel.Composition;
using Microsoft.Practices.Prism.Commands;
using CodeC.Interfaces;
using CodeC.Utilities;
using System.CodeDom.Compiler;
using System.Collections.ObjectModel;
using CodeC.Models;
using Microsoft.Practices.Prism.Events;
using CodeC.Infrastructure.Events;
using EnvDTE;
using System.Reflection;
using VSLangProj;

namespace CodeC.UI.ViewModel
{
    /// <summary>
    /// This CodeCViewModel is taken care of all the operations of the user input/output
    /// </summary>
    [Export(typeof(ICodeCViewModel))]
    public class CodeCViewModel : ViewModelBase, ICodeCViewModel
    {
        #region Private Variables

        private int _mainCodeBase = 1;
        private int _classMethodsCodeBase = 2;
        private int _classMethodsCodeLineCount = 0;
        private ICodeCModel _codeCModel;
        private CodeCHelper _codeCHelper = null;

        [Import]
        private IEventAggregator _eventAggregator = null;

        #endregion

        #region ctor
        [ImportingConstructor]
        public CodeCViewModel(ICodeCView view, ICodeCModel codeCModel, CodeCHelper codeCHelper)
        {
            _codeCModel = codeCModel;
            _codeCHelper = codeCHelper;
            this.View = view;
            this.View.SetView(this);
            LoadFileModel();
        }

        #endregion

        #region Public Properties

        public Interfaces.UI.Base.IBaseView View
        {
            get;
            set;
        }

        public string MainCode
        {
            get
            {
                return _codeCModel.MainCode ?? "";
            }
            set
            {
                if (value != _codeCModel.MainCode)
                {
                    _codeCModel.MainCode = value;
                    base.OnPropertyChanged(() => MainCode);
                    CommandRefresh();
                }
            }
        }

        public string ClassMethodsCode
        {
            get
            {
                return _codeCModel.ClassMethodsCode ?? "";
            }
            set
            {
                if (value != _codeCModel.ClassMethodsCode)
                {
                    _codeCModel.ClassMethodsCode = value;
                    base.OnPropertyChanged(() => ClassMethodsCode);
                    CommandRefresh();

                }
            }
        }

        private string _result;
        public string Result
        {
            get { return _result; }
            set
            {
                if (value != _result)
                {
                    _result = value;
                    base.OnPropertyChanged(() => Result);

                }
            }
        }

        private ObservableCollection<IErrorModel> _listOfError;
        public ObservableCollection<IErrorModel> ListOfError
        {
            get
            {
                return _listOfError;
            }
            set
            {
                if (value != _listOfError)
                {
                    _listOfError = value;
                    base.OnPropertyChanged(() => ListOfError);

                    base.OnPropertyChanged(() => IsError);
                    base.OnPropertyChanged(() => IsResult);

                }
            }
        }

        private IErrorModel _selectedErrorModel;
        public IErrorModel SelectedErrorModel
        {
            get { return _selectedErrorModel; }
            set
            {
                _selectedErrorModel = value;
                base.OnPropertyChanged(() => SelectedErrorModel);

                if (null != _selectedErrorModel)
                    _eventAggregator.GetEvent<ErrorSelectionChangedEvent<IErrorModel>>().Publish(_selectedErrorModel);


            }
        }

        private List<ICodeCFileModel> _listOfFileModel;
        public List<ICodeCFileModel> ListOfFileModel
        {
            get { return _listOfFileModel; }
            set
            {
                if (value != _listOfFileModel)
                {
                    _listOfFileModel = value;
                    base.OnPropertyChanged(() => ListOfFileModel);

                }
            }
        }

        private ICodeCFileModel _fileModel;
        public ICodeCFileModel FileModel
        {
            get { return _fileModel; }
            set
            {
                if (value != _fileModel)
                {
                    _fileModel = value;
                    if (null != _fileModel)
                    {
                        _codeCModel = _codeCHelper.GetCodeModel(value);
                        base.OnPropertyChanged(() => MainCode);
                        base.OnPropertyChanged(() => ClassMethodsCode);
                    }
                    CommandRefresh();
                    base.OnPropertyChanged(() => FileModel);
                }
            }
        }

        private string _newName;
        public string NewName
        {
            get { return _newName; }
            set
            {
                if (value != _newName)
                {
                    _newName = value;
                    CommandRefresh();
                    base.OnPropertyChanged(() => NewName);
                }
            }
        }

        public System.IServiceProvider ServiceProvider
        {
            get;
            set;
        }


        private bool _isExcludeProjectReference;

        public bool IsExcludeProjectReference
        {
            get { return _isExcludeProjectReference; }
            set
            {
                _isExcludeProjectReference = value;
                base.OnPropertyChanged(() => IsExcludeProjectReference);
            }
        }


        #endregion

        #region Command

        private DelegateCommand _runCommand;
        public DelegateCommand RunCommand
        {
            get
            {
                return _runCommand ??
                    (_runCommand = new DelegateCommand(
                        () => this.RunCode(),
                        () => this.CanRunCode
                        ));
            }
        }

        private DelegateCommand _saveCommand;
        public DelegateCommand SaveCommand
        {
            get
            {
                return _saveCommand ??
                        (_saveCommand = new DelegateCommand(
                            () => this.SaveCode(),
                            () => this.CanSaveCode
                            ));
            }
        }

        private DelegateCommand _deleteCommand;
        public DelegateCommand DeleteCommand
        {
            get
            {
                return _deleteCommand ??
                    (_deleteCommand = new DelegateCommand(
                        () => this.DeleteFile(),
                        () => this.CanDeleteFile
                        ));
            }
        }

        #endregion

        #region Predicator

        public bool CanRunCode
        {
            get
            {
                return _codeCModel.MainCode.IsNotNullOrNotEmpty();
            }
        }

        public bool IsResult
        {
            get { return !IsError; }

        }

        public bool IsError
        {
            get
            {
                return null != _listOfError;
            }
        }

        public bool CanSaveCode
        {
            get
            {
                return _newName.IsNotNullOrNotEmpty();
            }
        }

        public bool CanDeleteFile
        {
            get
            {
                return null != _fileModel
                            && _fileModel.ID > 0
                            && ListOfFileModel.Where(x => x.ID == _fileModel.ID && x.Name == _newName).Count() == 1;
            }
        }
        #endregion

        #region Private Methods

        private Tuple<ObservableCollection<IErrorModel>, bool> GetListOfError(Tuple<CompilerErrorCollection, bool> tupleCollection)
        {
            ObservableCollection<IErrorModel> listOfError = null;
            CompilerErrorCollection compilerErrorCollection = tupleCollection.Item1;
            bool isRunNamespaceError = tupleCollection.Item2;
            int namespaceLineCount = 0;

            if (null != compilerErrorCollection && 0 < compilerErrorCollection.Count)
            {

                if (null != _codeCModel && null != _codeCModel.ListOfNamespaces)
                    namespaceLineCount = _codeCModel.ListOfNamespaces.Count;

                List<string> listOfRemoves = new List<string>();
                if (isRunNamespaceError)
                {
                    isRunNamespaceError = false;
                    foreach (CompilerError item in compilerErrorCollection)
                    {
                        if (item.ErrorNumber == "CS0234" && item.ErrorText.Contains("The type or namespace name"))
                        {
                            listOfRemoves.Add(_codeCModel.ListOfNamespaces[item.Line - 1]);
                        }
                    }

                    if (listOfRemoves.Count > 0)
                    {
                        foreach (var item in listOfRemoves)
                        {
                            _codeCModel.ListOfNamespaces.Remove(item);
                        }
                        isRunNamespaceError = true;
                    }


                }

                if (!isRunNamespaceError)
                {
                    listOfError = new ObservableCollection<IErrorModel>();
                    foreach (CompilerError item in compilerErrorCollection)
                    {
                        listOfError.Add(new ErrorModel
                        {
                            Line = item.Line < (_classMethodsCodeLineCount + namespaceLineCount) ? item.Line - (_mainCodeBase + namespaceLineCount) : item.Line - (_classMethodsCodeLineCount + namespaceLineCount),
                            Section = item.Line < (_classMethodsCodeLineCount + namespaceLineCount) ? "Main" : "Class/Method",
                            Number = item.ErrorNumber,
                            Text = ((item.Line < (_classMethodsCodeLineCount + namespaceLineCount) ? item.Line - (_mainCodeBase + namespaceLineCount) : item.Line - (_classMethodsCodeLineCount + namespaceLineCount)) < 0 ? "CHECK EXCLUDE PROJECT REFERENCE & TRY AGAIN | " : "") + item.ErrorText.ReplaceCONSOLEREAD()
                        }
                            );

                    }
                }

                if (null != listOfError
                    && listOfError.Count > 0
                    && listOfError.Where(x => x.Line > 0).Count() > 0
                    && listOfError.Where(x => x.Line < 1).Count() > 0)
                {
                    var list = listOfError.Where(x => x.Line < 1).ToList();
                    foreach (var item in list)
                    {
                        listOfError.Remove(item);
                    }
                }
            }
            else
                isRunNamespaceError = false;


            return Tuple.Create(listOfError, isRunNamespaceError);

        }

        public void GetListOfReferences(System.IServiceProvider serviceProvider)
        {
            List<string> listOfReferences = new List<string>();
            if (null != serviceProvider && !IsExcludeProjectReference)
            {

                EnvDTE80.DTE2 dte = (EnvDTE80.DTE2)serviceProvider.GetService(typeof(Microsoft.VisualStudio.Shell.Interop.SDTE));
                if (null != dte && null != dte.Solution)
                    foreach (Project item in dte.Solution.Projects)
                        try { ReferencesLoad(item, ref listOfReferences); }
                        catch (Exception) { }

                _codeCModel.ListOfReferences = listOfReferences;
                _codeCModel.ListOfNamespaces = GetListOfNamespaces(listOfReferences);
            }

            if (null != listOfReferences && listOfReferences.Count == 0)
            {
                #region Default List Of References
                _codeCModel.ListOfReferences = new List<string>{
                        "System.dll",
                        "System.Core.dll",
                        "System.Xml.Linq.dll",
                        "System.Data.DataSetExtensions.dll",
                        "Microsoft.CSharp.dll",
                        "System.Data.dll",
                        "System.Xml.dll",
                        "mscorlib.dll"};
                #endregion

                #region Default List Of Namespaces
                _codeCModel.ListOfNamespaces = new List<string>{
                        "System.Text.RegularExpressions",
                        "System.CodeDom",
                        "System.ComponentModel",
                        "System.ComponentModel.Design",
                        "System.ComponentModel.Design.Serialization",
                        "System.Diagnostics",
                        "System.CodeDom.Compiler",
                        "System.Configuration",
                        "System.IO.Compression",
                        "System.IO",
                        "System.Runtime.InteropServices",
                        "System.Runtime.Versioning",
                        "System.Threading",
                        "System.Collections.Concurrent",
                        "System.Media",
                        "System.Security.AccessControl",
                        "System.Collections.Generic",
                        "System",
                        "System.Collections.Specialized",
                        "System.Collections.ObjectModel",
                        "Microsoft.Win32",
                        "System.Runtime.InteropServices.ComTypes",
                        "Microsoft.Win32.SafeHandles",
                        "Microsoft.CSharp",
                        "Microsoft.VisualBasic",
                        "System.Security.Permissions",
                        "System.Security.Cryptography",
                        "System.Security.Cryptography.X509Certificates",
                        "System.Security.Authentication.ExtendedProtection",
                        "System.Security.Authentication.ExtendedProtection.Configuration",
                        "System.Net",
                        "System.Net.Sockets",
                        "System.Net.Security",
                        "System.Net.Cache",
                        "System.Security.Authentication",
                        "System.Net.NetworkInformation",
                        "System.Net.Configuration",
                        "System.Net.Mail",
                        "System.Net.Mime",
                        "System.Configuration.Internal",
                        "System.Timers",
                        "System.Web",
                        "System.Diagnostics.CodeAnalysis",
                        "System.IO.Ports",
                        "System.Diagnostics.Eventing",
                        "System.Management.Instrumentation",
                        "System.Diagnostics.Eventing.Reader",
                        "System.Linq.Expressions",
                        "System.Linq",
                        "System.Runtime.CompilerServices",
                        "System.Linq.Parallel",
                        "System.Security",
                        "System.Security.Cryptography.Xml",
                        "System.Dynamic",
                        "System.Dynamic.Utils",
                        "System.Linq.Expressions.Compiler",
                        "System.Diagnostics.PerformanceData",
                        "System.IO.Pipes",
                        "System.Threading.Tasks",
                        "System.IO.MemoryMappedFiles",
                        "System.Xml.Linq",
                        "System.Xml.XPath",
                        "System.Xml.Schema",
                        "MS.Internal.Xml.Linq.ComponentModel",
                        "System.Data.DataSetExtensions",
                        "System.Data",
                        "Microsoft.CSharp.RuntimeBinder",
                        "Microsoft.CSharp.RuntimeBinder.Semantics",
                        "Microsoft.CSharp.RuntimeBinder.Errors",
                        "Microsoft.CSharp.RuntimeBinder.Syntax",
                        "Microsoft.SqlServer.Server",
                        "System.Data.Common",
                        "System.Data.ProviderBase",
                        "System.Data.Odbc",
                        "System.Data.OleDb",
                        "System.Data.Sql",
                        "System.Data.SqlClient",
                        "System.Data.SqlTypes",
                        "System.Xml",
                        "MS.Internal.Xml.Cache",
                        "System.Xml.Resolvers",
                        "MS.Internal.Xml.XPath",
                        "System.Xml.Xsl",
                        "System.Xml.Serialization",
                        "System.Xml.Serialization.Advanced",
                        "System.Xml.Serialization.Configuration",
                        "System.Runtime.Serialization",
                        "System.Collections",
                        "System.Text",
                        "System.Runtime.Hosting",
                        "System.Security.Policy",
                        "System.Deployment.Internal",
                        "System.Runtime.ConstrainedExecution",
                        "System.Reflection",
                        "System.StubHelpers",
                        "System.Runtime.Remoting",
                        "System.Deployment.Internal.Isolation.Manifest",
                        "System.Deployment.Internal.Isolation",
                        "System.Diagnostics.Contracts",
                        "System.Diagnostics.Contracts.Internal",
                        "System.Diagnostics.SymbolStore",
                        "Microsoft.Runtime.Hosting",
                        "System.Runtime.ExceptionServices",
                        "System.Globalization",
                        "System.Resources",
                        "System.Security.Util",
                        "System.Security.Principal",
                        "System.Runtime.InteropServices.Expando",
                        "System.Runtime",
                        "System.Runtime.Remoting.Activation",
                        "System.Runtime.Remoting.Contexts",
                        "System.Runtime.Remoting.Messaging",
                        "System.Runtime.Remoting.Channels",
                        "System.Runtime.Remoting.Lifetime",
                        "System.Runtime.Remoting.Services",
                        "System.Runtime.Serialization.Formatters",
                        "System.Runtime.Remoting.Proxies",
                        "System.Runtime.Remoting.Metadata",
                        "System.Runtime.Remoting.Metadata.W3cXsd2001",
                        "System.IO.IsolatedStorage",
                        "System.Runtime.Serialization.Formatters.Binary",
                        "System.Reflection.Emit",
                        "System.Reflection.Cache",
                        "System.Configuration.Assemblies",
                        "System.Runtime.InteropServices.TCEAdapterGen"
                        };
                #endregion
            }


        }

        private List<string> GetListOfNamespaces(List<string> listOfReferences)
        {
            List<string> listOfNamespaces = new List<string>();
            foreach (var item in listOfReferences)
            {
                try
                {
                    var assembly = Assembly.LoadFile(item);
                    foreach (var assemblyType in assembly.GetTypes())
                    {
                        if (!listOfNamespaces.Contains(assemblyType.Namespace)
                            && null != assemblyType.Namespace
                            && !assemblyType.Namespace.Contains("<"))
                        {
                            if (assemblyType.Namespace.Contains("CSharp"))
                            {
                                string str = assemblyType.Name;
                            }
                            listOfNamespaces.Add(assemblyType.Namespace);

                        }
                    }
                }
                catch (Exception) { }
            }
            return listOfNamespaces;

        }

        private bool ReferencesLoad(Project project, ref List<string> listOfReferences)
        {
            if (null == project)
                return false;

            if (project.FullName.IsNullOrEmpty())
                for (int i = 1; i <= project.ProjectItems.Count; i++)
                { ReferencesLoad(project.ProjectItems.Item(i).SubProject, ref listOfReferences); }

            VSLangProj.VSProject vsProject = (VSLangProj.VSProject)project.Object;

            References references = vsProject.References;
            int length = references.Count;
            List<string> listOfNamespaces = new List<string>();
            for (int i = 1; i <= length; i++)
            {
                if (listOfReferences.Where(x => x.Contains(@"\" + System.IO.Path.GetFileName(references.Item(i).Path))).Count() == 0
                    && !references.Item(i).Path.Contains(@"\mscorlib.dll"))
                    listOfReferences.Add(references.Item(i).Path);
            }

            return true;
        }

        private void DeleteFile()
        {
            try
            {
                System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;
                _codeCHelper.RemoveCode(FileModel);
                LoadFileModel();
                base.OnPropertyChanged(() => FileModel);
            }
            finally
            {
                System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default;
            }
        }

        private void LoadFileModel()
        {
            ListOfFileModel = _codeCHelper.GetListOfFileModel();
        }

        private void SaveCode()
        {
            try
            {
                System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;

                if (null == _fileModel || _fileModel.ID == 0)
                {
                    _fileModel = new CodeCFileModel { ID = 0, Name = _newName };

                }
                var fileModel = _codeCHelper.SaveCode(_codeCModel, _fileModel);
                LoadFileModel();

                FileModel = ListOfFileModel.Where(x => x.ID == fileModel.ID).FirstOrDefault();
                base.OnPropertyChanged(() => FileModel);
            }
            finally
            {
                System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default;
            }

        }

        private void RunCode()
        {
            try
            {
                System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;
                GetListOfReferences(ServiceProvider);
                bool isRunNamespaceError = true;
                for (int i = 0; i < 2 && isRunNamespaceError; i++)
                {

                    IOutputModel<string> output = _codeCHelper.Run(_codeCModel);
                    _classMethodsCodeLineCount = _classMethodsCodeBase + MainCode.LineCount();
                    var tupleCollection = GetListOfError(Tuple.Create(output.Errors, isRunNamespaceError));
                    // ListOfError = GetListOfError(output.Errors);
                    isRunNamespaceError = tupleCollection.Item2;
                    if (!isRunNamespaceError)
                    {
                        ListOfError = tupleCollection.Item1;
                        Result = output.Result;
                        return;
                    }

                }

            }
            catch (Exception ex)
            {
                System.Windows.MessageBox.Show(ex.Message);
            }
            finally
            {
                System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default;
            }
        }

        private void CommandRefresh()
        {
            RunCommand.RaiseCanExecuteChanged();
            SaveCommand.RaiseCanExecuteChanged();
            DeleteCommand.RaiseCanExecuteChanged();
        }
        #endregion

    }
}
